package jfoo.detailsTable;

import java.awt.Component;
import java.awt.Point;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import java.awt.event.InputEvent;
import java.awt.event.MouseEvent;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.Reader;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;
import java.util.Vector;

import javax.swing.JComponent;
import javax.swing.JTable;
import javax.swing.TransferHandler;

import audio.AudioFile;
import audio.AudioFileListTransferable;
import audio.AudioList;
import audio.MP3;

// inspired by http://iharder.sourceforge.net/current/java/filedrop/

// http://forums.sun.com/thread.jspa?threadID=497065&messageID=2431829
//http://java.sun.com/j2se/1.4.2/docs/guide/dragndrop/spec/dnd1.html#wp924198    Transferring Data across the JVM boundary 

public class DetailsTableTransferHandler extends TransferHandler {

	private DetailsTable detailsTable;

	protected Point dragPoint;
	protected Point dropPoint;
	protected Component dragComponent;
	protected Component dropComponent;

	public AudioFileListTransferable audioFileListTransferable;

	DetailsTableTransferHandler(DetailsTable detailsTable) {
		this.detailsTable = detailsTable;
	}

	public Point getDropPoint() {
		return dropPoint;
	}

	public void setDropPoint(Point dropPoint) {
		this.dropPoint = dropPoint;
	}

	public Component getDragComponent() {
		return dragComponent;
	}

	public void setDragComponent(Component dragComponent) {
		this.dragComponent = dragComponent;
		System.out.println("export as drag" + dragComponent);
	}

	public Point getDragPoint() {
		return dragPoint;
	}

	public void setDragPoint(Point dragPoint) {
		this.dragPoint = dragPoint;
	}

	public Component getDropComponent() {
		return dropComponent;
	}

	public void setDropComponent(Component dropComponent) {
		this.dropComponent = dropComponent;
	}

	public int getSourceActions(JComponent c) {
		return DnDConstants.ACTION_COPY_OR_MOVE;
	}

	public void exportAsDrag(JComponent comp, InputEvent e, int action) {
		setDragComponent(comp);
		setDragPoint(((MouseEvent) e).getPoint());
		super.exportAsDrag(comp, e, action);
	}

	// This method bundles up the data to be exported into a Transferable
	// object in preparation for the transfer.
	protected Transferable createTransferable(JComponent c) {
		// detailsTable = (DetailsTable) c;
		int[] index = detailsTable.getSelectedRows();
		Vector<AudioFile> selectedRows = new Vector<AudioFile>();
		for (int i = 0; i < index.length; i++) {
			selectedRows.add((AudioFile) detailsTable.getDetailsTableModel().getElementAt(index[i]));
		}
		audioFileListTransferable = new AudioFileListTransferable(selectedRows);
		return audioFileListTransferable;
	}

	// This method is invoked after the export is complete. When the action
	// is a
	// MOVE, the data needs to be removed from the source after the transfer
	// is
	// complete — this method is where any necessary cleanup occurs.
	protected void exportDone(JComponent source, Transferable data, int action) {

		if (action == DnDConstants.ACTION_MOVE) {
			try {
				List<AudioFile> list = (List<AudioFile>) data.getTransferData(AudioFileListTransferable.AUDIOFILE_LIST_FLAVOR);

				if (source != getDropComponent()) {
					DetailsTableModel model = detailsTable.getDetailsTableModel();
					for (int i = 0; i < list.size(); i++) {
						model.removeRow(list.get(i));
						// TODO : and what's going on when there are several copies of the same AudioFile in that playlist :/
					}
				} else { // nothing to do, handled in dragOver(DropTargetDragEvent e) {

				}

				// update the current playing display
				AudioFile o = detailsTable.getControlCenter().getCurrentAudioFile();
				if (list.contains(o)) {
					detailsTable.handleCurrentPlaying(o);
				}

			} catch (Exception e) {
				e.printStackTrace();
			}
		} else if (action == DnDConstants.ACTION_COPY) {
			if (source == getDropComponent()) {
				try {

					List<AudioFile> list = (List<AudioFile>) data.getTransferData(AudioFileListTransferable.AUDIOFILE_LIST_FLAVOR);
					JTable table = (JTable) source;
					DetailsTableModel model = ((DetailsTableModel) table.getModel());
					int insertRow = table.rowAtPoint(getDropPoint());
					if (insertRow == -1) {
						insertRow = model.getRowCount();

					}
					System.out.println(insertRow);
					model.addAudioFiles(list, insertRow);

				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}

	}

	public DropHandler getDropHandler() {

		return this.new DropHandler();
	}

	public boolean canImport(JComponent comp, DataFlavor[] transferFlavors) {
		if (comp.isEnabled() && (comp instanceof DetailsTable)) {
			for (int i = 0; i < transferFlavors.length; i++) {
				if ((transferFlavors[i].equals(AudioFileListTransferable.AUDIOFILE_LIST_FLAVOR) || (transferFlavors[i]
						.equals(java.awt.datatransfer.DataFlavor.javaFileListFlavor)))
						|| (transferFlavors[i].equals(java.awt.datatransfer.DataFlavor.stringFlavor))) {
					return true;
				}
			}
		}
		return false;
	}

	public static void scan(File f, AudioList audioFiles) {
		if (f.isDirectory()) {
			File[] liste = f.listFiles();
			for (int i = 0; i < liste.length; i++) {
				scan(liste[i], audioFiles);
			}
		} else {
			if (AudioFile.isAudioFile(f)) {
				try {
					MP3 m = new MP3(f);
					audioFiles.add(m);

					
				} catch (IOException e) {
					System.out.println(f.getPath() + "couldn't be found");

				}
			}
		}
	}

	// public boolean importData(TransferHandler.TransferSupport support) {
	public boolean importData(JComponent comp, Transferable tr) {

		if (!canImport(comp, tr.getTransferDataFlavors())) {
			return false;
		}

		int insertRow;
		if (getDropPoint() != null) {
			insertRow = detailsTable.rowAtPoint(getDropPoint());

			if (insertRow == -1) {
				insertRow = detailsTable.getModel().getRowCount() - 1; // insert at the end
			}

		} else {
			insertRow = detailsTable.getSelectedRow();
			if (insertRow == -1) {
				insertRow = detailsTable.getModel().getRowCount() - 1; // insert at the end
			}
		}

		// should be used with jre 1.6 with importData(TransferHandler.TransferSupport support) { :
		// JTable.DropLocation dl = (JTable.DropLocation) support.getDropLocation();
		// int index = dl.getRow();

		AudioList audioFiles = new AudioList();
		ArrayList<File> fileList = new ArrayList<File>();
		try {
			// java.awt.datatransfer.Transferable tr = support.getTransferable();
			if (getDragComponent() != comp) {
				if (tr.isDataFlavorSupported(AudioFileListTransferable.AUDIOFILE_LIST_FLAVOR)) {
					System.out.println("AudioFileList transferable supported !!!");
					List<AudioFile> list = (List<AudioFile>) tr.getTransferData(AudioFileListTransferable.AUDIOFILE_LIST_FLAVOR);

					detailsTable.getDetailsTableModel().addAudioFiles(list, insertRow);
					return true;
				}
			}

			// Is it a file list?
			if (tr.isDataFlavorSupported(java.awt.datatransfer.DataFlavor.javaFileListFlavor)) {
				// Say we'll take it.
				// evt.acceptDrop (
				// java.awt.dnd.DnDConstants.ACTION_COPY_OR_MOVE );
				// evt.acceptDrop(java.awt.dnd.DnDConstants.ACTION_COPY);

				// Get a useful list
				fileList.addAll((List<File>) tr.getTransferData(java.awt.datatransfer.DataFlavor.javaFileListFlavor));

			} // end if: file list
			else // this section will check for a reader flavor.
			{
				// Thanks, Nathan!
				// BEGIN 2007-09-12 Nathan Blomquist -- Linux (KDE/Gnome)
				// support added.
				DataFlavor[] flavors = tr.getTransferDataFlavors();
				boolean handled = false;
				for (int zz = 0; zz < flavors.length; zz++) {
					if (flavors[zz].isRepresentationClassReader()) {
						// Say we'll take it.
						// evt.acceptDrop (
						// java.awt.dnd.DnDConstants.ACTION_COPY_OR_MOVE );
						// evt.acceptDrop(java.awt.dnd.DnDConstants.ACTION_COPY);

						Reader reader = flavors[zz].getReaderForText(tr);

						BufferedReader br = new BufferedReader(reader);

						fileList = createFileArray(br);
						// Mark that drop is completed.
						// evt.getDropTargetContext().dropComplete(true);

						handled = true;
						break;
						// END 2007-09-12 Nathan Blomquist -- Linux (KDE/Gnome)
						// support
					} else {
						handled = false;

					}
				}
				if (!handled) { // the source may come from JAVA TODO : fix that
					// dirty way. don't know how
					if (tr.isDataFlavorSupported(DataFlavor.stringFlavor)) { // Linux
						String urls = (String) tr.getTransferData(DataFlavor.stringFlavor);
						fileList = new ArrayList<File>();
						for (StringTokenizer st = new StringTokenizer(urls, "\r\n"); st.hasMoreTokens();) {
							fileList.add(new File(st.nextToken()));

						}

					}

					handled = true;
				}

				if (!handled) {
					System.out.println("the drag and drop failed, probably due to a flavor thing");
					return false;
				}

			}
		} catch (Exception exc) {
			exc.printStackTrace();
		}

		Iterator<File> iterator = fileList.iterator();

		while (iterator.hasNext()) {
			scan(iterator.next(), audioFiles);
		}

		detailsTable.getDetailsTableModel().addAudioFiles(audioFiles, insertRow + 1);
		// Rectangle rect = detailsTable.getCellRect(index, 0, false);
		// if (rect != null) {
		// detailsTable.scrollRectToVisible(rect);
		// }

		return true;
	}

	private static String ZERO_CHAR_STRING = "" + (char) 0;

	private static ArrayList<File> createFileArray(BufferedReader bReader) {
		try {
			ArrayList<File> list = new ArrayList<File>();
			String line = null;
			while ((line = bReader.readLine()) != null) {
				try {

					// kde seems to append a 0 char to the end of the reader
					if (ZERO_CHAR_STRING.equals(line))
						continue;

					java.io.File file = new java.io.File(new java.net.URI(line));
					// System.out.println(file.getAbsolutePath());
					list.add(file);
				} catch (Exception ex) {

					System.out.println("Error with " + line + ": " + ex.getMessage());
				}
			}

			return list;
		} catch (IOException ex) {
			System.out.println("FileDrop: IOException");
		}
		return new ArrayList<File>();
	}

	// END 2007-09-12 Nathan Blomquist -- Linux (KDE/Gnome) support added.

	protected class DropHandler implements DropTargetListener, Serializable {

		private boolean canImport;

		private boolean actionSupported(int action) {
			return (action & (DnDConstants.ACTION_COPY_OR_MOVE | DnDConstants.ACTION_LINK)) != DnDConstants.ACTION_NONE;
		}

		// --- DropTargetListener methods -----------------------------------

		int oldRow;

		public void dragEnter(DropTargetDragEvent e) {
			oldRow = detailsTable.rowAtPoint(e.getLocation());
			DataFlavor[] flavors = e.getCurrentDataFlavors();

			JComponent c = (JComponent) e.getDropTargetContext().getComponent();
			TransferHandler importer = c.getTransferHandler();

			if (importer != null && importer.canImport(c, flavors)) {
				canImport = true;
			} else {
				canImport = false;
			}

			int dropAction = e.getDropAction();

			if (canImport && actionSupported(dropAction)) {
				e.acceptDrag(dropAction);
			} else {
				e.rejectDrag();
			}
		}

		public void dragOver(DropTargetDragEvent e) {

			int dropAction = e.getDropAction();

			if (canImport && actionSupported(dropAction)) {

				int row = detailsTable.rowAtPoint(e.getLocation());

				if (row != oldRow) {
					oldRow = row;
					if ((row == -1) && (detailsTable.getRowCount() != 0)) {
						row = detailsTable.getRowCount() - 1; // we add the file at the end of the table
					}

					e.acceptDrag(dropAction);

					if (dropAction == DnDConstants.ACTION_MOVE) {
						List<AudioFile> list = null;
						try {
							list = (List<AudioFile>) audioFileListTransferable
									.getTransferData(AudioFileListTransferable.AUDIOFILE_LIST_FLAVOR);
						} catch (UnsupportedFlavorException e1) {
							e1.printStackTrace();
						} catch (IOException e1) {
							e1.printStackTrace();
						} catch (NullPointerException e1) {
							return;  // happens on Windows
						}

						DetailsTableModel model = detailsTable.getDetailsTableModel();
						// if (getDragComponent() == detailsTable) {

						int index;
						int insertRow = row;
						if (insertRow == -1) {// the playlist is empty, the dragsource is outside the JVM
							model.addAudioFiles(list, 0);
						} else {
							for (int i = 0; i < list.size(); i++) {
								index = model.getAudioFiles().indexOf(list.get(i));
								model.moveRow(index, insertRow + i);
							}
						}

					}
					if (row != -1) {
						detailsTable.setRowSelectionInterval(row, row);
					} else {
						detailsTable.setRowSelectionInterval(0, 0);
					}

				}

			} else {
				e.rejectDrag();
			}

		}

		public void dragExit(DropTargetEvent e) {
		}

		public void drop(DropTargetDropEvent e) {
			int dropAction = e.getDropAction();

			JComponent c = (JComponent) e.getDropTargetContext().getComponent();
			DetailsTableTransferHandler importer = (DetailsTableTransferHandler) c.getTransferHandler();

			if (canImport && importer != null && actionSupported(dropAction)) {
				e.acceptDrop(dropAction);
				try {
					Transferable t = e.getTransferable();
					importer.setDropPoint(e.getLocation());
					importer.setDropComponent(c);
					e.dropComplete(importer.importData(c, t));

				} catch (RuntimeException re) {
					e.dropComplete(false);
				}
			} else {
				e.rejectDrop();
			}
		}

		public void dropActionChanged(DropTargetDragEvent e) {
			int dropAction = e.getDropAction();

			if (canImport && actionSupported(dropAction)) {
				e.acceptDrag(dropAction);
			} else {
				e.rejectDrag();
			}
		}
	}

}